<script setup lang="ts">
// 导入Vue相关功能
import { computed, ref, onMounted, onUnmounted } from 'vue'

// 定义通知组件的属性接口
interface NotificationProps {
  // 控制通知是否显示
  visible: boolean
  // 通知类型：成功、错误、警告、信息
  type?: 'success' | 'error' | 'warning' | 'info'
  // 通知显示位置
  placement?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
  // 通知标题
  title?: string
  // 通知内容
  message?: string
  // 是否显示关闭按钮
  closable?: boolean
  // 自动关闭时间（毫秒），0表示不自动关闭
  duration?: number
  // 鼠标悬停时是否暂停倒计时
  pauseOnHover?: boolean
  // 是否显示图标
  showIcon?: boolean
  // 自定义偏移量
  offset?: { x?: number; y?: number }
  // 过渡动画类型，支持预定义值和自定义字符串
  transition?: 'ease' | 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out' | string
  // 过渡动画持续时间（毫秒）
  transitionDuration?: number
}

// 定义组件属性的默认值和类型
const props = withDefaults(defineProps<NotificationProps>(), {
  visible: false,
  type: 'info',
  placement: 'top-right',
  title: '',
  message: '',
  closable: true,
  duration: 4500,
  pauseOnHover: true,
  showIcon: true,
  offset: () => ({ x: 0, y: 0 }),
  transition: 'ease',
  transitionDuration: 300
})

// 定义组件发出的事件
interface NotificationEmits {
  // 关闭事件
  (e: 'close'): void
  // 更新visible状态事件
  (e: 'update:visible', visible: boolean): void
}

// 定义组件发出的事件
const emit = defineEmits<NotificationEmits>()

// 定时器引用
const timer = ref<number | null>(null)
// 鼠标是否悬停
const isHovering = ref(false)


// 定义预设的动画效果时间函数
const presetTransitions = {
  // 标准缓动
  ease: 'ease',
  // 线性
  linear: 'linear',
  // 缓入
  'ease-in': 'ease-in',
  // 缓出
  'ease-out': 'ease-out',
  // 缓入缓出
  'ease-in-out': 'ease-in-out',
  // 贝塞尔曲线（自定义缓动）
  'cubic-bezier': 'cubic-bezier(0.25, 0.8, 0.25, 1)',
  // 回弹效果
  'back': 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
  // 圆形缓动
  'circ': 'cubic-bezier(0.1, 0.57, 0.1, 1)',
  // 指数缓动
  'expo': 'cubic-bezier(0.19, 1, 0.22, 1)',
  // 四次方缓动
  'quart': 'cubic-bezier(0.165, 0.84, 0.44, 1)',
  // 五次方缓动
  'quint': 'cubic-bezier(0.23, 1, 0.32, 1)',
  // 正弦缓动
  'sine': 'cubic-bezier(0.445, 0.05, 0.55, 0.95)'
}

// 计算通知的样式
const notificationStyle = computed(() => {
  // 获取对应的动画函数
  // 如果是预设值则使用预设，否则使用用户自定义的值
  const timingFunction =
      presetTransitions[props.transition as keyof typeof presetTransitions] ||
      props.transition

  // 返回样式对象
  return {
    // 应用自定义偏移量
    transform: `translate(${props.offset.x || 0}px, ${props.offset.y || 0}px)`,
    // 设置过渡动画
    transition: `opacity ${props.transitionDuration}ms ${timingFunction},
                 transform ${props.transitionDuration}ms ${timingFunction},
                 left ${props.transitionDuration}ms ${timingFunction},
                 right ${props.transitionDuration}ms ${timingFunction},
                 top ${props.transitionDuration + 100}ms ${timingFunction},
                 bottom ${props.transitionDuration}ms ${timingFunction}`
  }
})

// 开始倒计时
const startTimer = () => {
  // 如果设置了自动关闭时间且大于0
  if (props.duration > 0) {
    // 清除已存在的定时器
    if (timer.value) {
      clearTimeout(timer.value)
    }

    // 设置新的定时器
    timer.value = window.setTimeout(() => {
      // 只有在非悬停状态才关闭
      if (!isHovering.value) {
        close()
      }
    }, props.duration)
  }
}

// 清除倒计时
const clearTimer = () => {
  if (timer.value) {
    clearTimeout(timer.value)
    timer.value = null
  }
}

// 处理鼠标进入事件
const handleMouseEnter = () => {
  // 设置悬停状态
  isHovering.value = true

  // 如果启用了悬停暂停功能，清除定时器
  if (props.pauseOnHover) {
    clearTimer()
  }
}

// 处理鼠标离开事件
const handleMouseLeave = () => {
  // 清除悬停状态
  isHovering.value = false

  // 如果启用了悬停暂停功能，重新开始定时器
  if (props.pauseOnHover) {
    startTimer()
  }
}

// 关闭通知的方法
const close = () => {
  // 清除定时器
  clearTimer()

  // 发出关闭事件
  emit('close')
  // 更新visible状态
  emit('update:visible', false)
}

// 组件挂载时的处理
onMounted(() => {
  // 如果通知可见且设置了自动关闭时间，开始倒计时
  if (props.visible && props.duration > 0) {
    startTimer()
  }
})

// 组件卸载时的清理
onUnmounted(() => {
  // 清除定时器
  clearTimer()
})

// 监听visible属性变化
import { watch } from 'vue'
watch(
    () => props.visible,
    (newVal) => {
      if (newVal) {
        // 重新开始倒计时
        startTimer()
      } else {
        // 清除定时器
        clearTimer()
      }
    }
)
</script>

<template>
  <!-- 通知组件模板 -->
  <div
      class="notification"
      :class="[
      `notification-${type}`,  // 根据通知类型添加样式类
      `notification-${placement}`,  // 根据位置添加样式类
      { 'notification-show': visible }  // 根据显示状态添加类名
    ]"
      :style="notificationStyle"
      @mouseenter="handleMouseEnter"
      @mouseleave="handleMouseLeave"
  >
    <!-- 通知图标 -->
    <div class="notification-icon">
      <slot name="icon">
        <!-- 根据不同类型显示不同图标 -->
        <span v-if="type === 'success'">✓</span>
        <span v-else-if="type === 'error'">✗</span>
        <span v-else-if="type === 'warning'">!</span>
        <span v-else-if="type === 'info'">i</span>
      </slot>
    </div>

    <!-- 通知内容区域 -->
    <div class="notification-content">
      <!-- 标题 -->
      <div v-if="title" class="notification-title">{{ title }}</div>
      <!-- 内容 -->
      <div class="notification-message">
        <slot>{{ message }}</slot>
      </div>
    </div>

    <!-- 关闭按钮 -->
    <button
        v-if="closable"
        class="notification-close"
        @click="close"
    >
      ×
    </button>
  </div>
</template>

<style lang="scss" scoped>
// 通知组件样式

// 基础变量定义
$notification-z-index: 2000;
$notification-width: 320px;
$notification-padding: 16px;
$notification-border-radius: 4px;
$notification-box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);

// 通知类型颜色定义
$notification-colors: (
    success: #52c41a,
    error: #ff4d4f,
    warning: #faad14,
    info: #1890ff
);

// 通知容器基础样式
.notification {
  position: fixed;
  width: $notification-width;
  padding: $notification-padding;
  border-radius: $notification-border-radius;
  box-shadow: $notification-box-shadow;
  z-index: $notification-z-index;
  display: flex;
  align-items: flex-start;
  // 初始状态透明且缩小
  opacity: 0;
  will-change: opacity, transform, left, right, top, bottom;

  // 不同位置的定位
  &.notification-top-left {
    top: 24px;
    left: 24px;
  }

  &.notification-top-right {
    top: 24px;
    right: 24px;
  }

  &.notification-bottom-left {
    bottom: 24px;
    left: 24px;
  }

  &.notification-bottom-right {
    bottom: 24px;
    right: 24px;
  }

  // 显示状态
  &.notification-show {
    opacity: 1;
    transform: scale(1);
  }

  // 不同类型的通知样式
  @each $type, $color in $notification-colors {
    &.notification-#{$type} {
      background: lighten($color, 45%);
      border-left: 4px solid $color;
    }
  }
}

// 通知图标样式
.notification-icon {
  margin-right: 12px;
  font-size: 16px;
  font-weight: bold;
  min-width: 20px;
  text-align: center;

  // 不同类型图标的颜色
  @each $type, $color in $notification-colors {
    .notification-#{$type} & {
      color: $color;
    }
  }
}

// 通知内容区域样式
.notification-content {
  flex: 1;
  min-width: 0;
}

// 通知标题样式
.notification-title {
  font-weight: 600;
  font-size: 14px;
  margin-bottom: 4px;
  color: #333;
}

// 通知消息样式
.notification-message {
  font-size: 13px;
  color: #666;
  line-height: 1.4;
}

// 通知关闭按钮样式
.notification-close {
  background: none;
  border: none;
  font-size: 18px;
  cursor: pointer;
  color: #999;
  margin-left: 12px;
  padding: 0;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.3s ease;

  // 鼠标悬浮时旋转效果
  &:hover {
    transform: rotate(90deg);
    color: #333;
  }
}
</style>